org 100h					; 程序加载到100h，可用于生成COM
; 设置时钟中断向量（08h），初始化段寄存器

	; clear screen
	mov ah, 06h     ; 功能号(向上滚动文本显示屏幕)
	mov al, 0
	mov ch, 0
	mov cl, 0
	mov dh, 24
	mov dl, 79
	mov bh, 0ch
	int 10h ; call 10H to print interrupt

	xor ax,ax					; AX = 0
	mov es,ax				; ES = 0
	mov word[es:20h],Timer	; 设置时钟中断向量的偏移地址
	mov ax,cs 
	mov [es:22h],ax			; 设置时钟中断向量的段地址=CS
	mov ds,ax				; DS = CS
	;mov es,ax				; ES = CS
; 在屏幕左上角显示字符‘A’	
	mov	ax,0B800h			; 文本窗口显存起始地址
	mov	gs,ax				; ES = B800h
	mov ah,0Fh				; 0000：黑底、1111：亮白字（默认值为07h）
	mov al,'A'				; AL = 显示字符值（默认值为20h=空格符）
	mov [gs:((80*0+0)*2)],ax	; 屏幕第0行, 第0列
	
	call SetTimer				; 设置计时器
	jmp $					; 死循环
	
; 时钟中断处理程序
	delay equ 10				; 计时器延迟计数
	count db delay				; 计时器计数变量，初值=delay
	; 字符位置及范围变量：
	x dw 0					; 字符位置x值，初值=0
	y db 0					; 字符位置y值，初值=0
	x1 db 0					; 字符位置x左端值x1，初值=0
	x2 db 79					; 字符位置x右端值x2，初值=79
	y1 db 0					; 字符位置y上端值y1，初值=0
	y2 db 24					; 字符位置y下端值y2，初值=24
	letter db 'A'				; 起始字母=A
	dir db 0					; 前进的方向，0=左->右、1=上->下、2=右->左、3=下->上
Timer: ; 时钟中断处理例程入口
	dec byte[count]			; 递减计数变量
	jnz LABEL_END			; >0：跳转
; 计算字母显示的偏移位置（BX = (80*y + x)*2 ）：
	mov al, 80				; AL = 80
	mul byte[y]				; AX = AL*y
	mov bx,ax				; BX = AX
	add bx,[x]				; BX += x
	shl bx,1					; BX << 1 （相当于BX*2）
	mov ah,0fh				; 设置字符的颜色：黑底、亮白
	mov al,[letter]				; AL = 字母的ASCII码
	mov [gs:bx],ax			; 在GS:BX =（x, y）处显示字母
LABEL_0: ; 前进的方向为0：左->右
	cmp byte[dir],0			; 判断dir == 0 ？
	jne LABEL_1				; ≠ 跳转到dir = 1
	inc byte[x]				; x++
	mov al,[x]				; AL = x
	cmp al,[x2]				; 判断 x < x2 ？
	jb LABEL_OUT			; 若 x < x2，继续当前方向的循环
	mov byte[dir],1			; 若 x ≥ x2，则置下一方向dir = 1
	jmp LABEL_OUT			; 进入新方向的循环
LABEL_1: ; 前进的方向为1：上->下
	cmp byte[dir],1			; 判断dir == 1 ？
	jne LABEL_2				; ≠ 跳转到dir = 2
	inc byte[y] 				; y++
	mov al,[y] 				; AL = y
	cmp al,[y2]				; 判断 y < y2 ？
	jb LABEL_OUT			; 若 y < y2，继续当前方向的循环
	mov byte[dir],2			; 若 y ≥ y2，则置下一方向dir = 2
	jmp LABEL_OUT			; 进入新方向的循环
LABEL_2: ; 前进的方向为2：右->左
	cmp byte[dir],2			; 判断dir == 2 ？
	jne LABEL_3				; ≠ 跳转到dir = 3
	dec byte[x] 				; x--
	mov al,[x] 				; AL =
	cmp al,[x1] 				; 判断 x > x1 ？
	ja LABEL_OUT			; 若 x > x1，继续当前方向的循环
	mov byte[dir],3			; 若 x ≤ x2，则置下一方向dir = 3
	jmp LABEL_OUT			; 进入新方向的循环
LABEL_3: ; 前进的方向为3：下->上
	cmp byte[dir],3			; 判断dir == 3 ？
	jne LABEL_OUT			; ≠ 跳转到下次循环
	dec byte[y] 				; y--
	mov al,[y] 				; AL = y
	cmp al,[y1] 				; 判断 y > y1 ？
	ja LABEL_OUT			; 若 y > y1，继续当前方向的循环
	mov byte[dir],0			; 若 y ≤ y2，则置下一方向dir = 0
; 准备进入下一圈循环：
	inc byte[letter]				; 字母++
	inc byte[x1]				; x1++
	inc byte[y1]				; y1++
	dec byte[x2]				; x2--
	dec byte[y2]				; y2--
	mov al,[y1]				; AL = y1
	cmp al,[y2]				; y1 ≥ y2 ？
	jae LABEL_EXIT			; 循环结束，退出程序。否则，进入下一圈循环：
	mov al,[x1]				; AL = x1
	mov [x],al				; x = AL = x1
	mov al,[y1]				; AL = y1
	mov [y],al				; y = AL = y1
LABEL_OUT: ; 重置延时初值
	mov byte[count],delay		; 重置计数变量=初值delay
LABEL_END: ; 发送EOI，结束本次中断处理
	mov al,20h				; AL = EOI
	out 20h,al					; 发送EOI到主8529A
	out 0A0h,al				; 发送EOI到从8529A
	iret						; 从中断返回

LABEL_EXIT: ; 发送EOI，结束程序运行
	mov al,20h				; AL = EOI
	out 20h,al					; 发送EOI到主8529A
	out 0A0h,al				; 发送EOI到从8529A
	; 终止程序，返回DOS
	mov	ax,4c00h		; AH=4Ch（功能号，终止进程）、AL=0（返回代码）
	int	21h			; DOS软中断
	
SetTimer: ; 设置计时器的时间间隔
	mov al,34h				; 设控制字值
	out 43h,al					; 写控制字到控制字寄存器
	mov ax,1193182/100		; 每秒100次中断（10ms一次）
	out 40h,al					; 写计数器0的低字节
	mov al,ah					; AL=AH
	out 40h,al					; 写计数器0的高字节
	ret